import fs from "node:fs/promises";
import path from "node:path";

const WORKFLOW_ROOT = path.resolve(__dirname, "..", "..");

export interface CoverageReport {
  fieldsExists: boolean;
  editExists: boolean;
  previewExists: boolean;
  coverage: {
    fieldsToEdit: number;
    fieldsToPreview: number;
  };
  orphanFields: string[];
  orphanEdits: string[];
}

export interface ArchitectureValidatorOptions {
  fieldsFile: string;
  editFile: string;
  previewFile: string;
}

async function readFileText(target: string): Promise<string> {
  const absolutePath = path.isAbsolute(target)
    ? target
    : path.join(WORKFLOW_ROOT, target);
  return fs.readFile(absolutePath, "utf-8");
}

function extractFieldNames(source: string): string[] {
  const matches = source.match(/name\s*:\s*["'`](.+?)["'`]/g) || [];
  const names = matches.map((match) => {
    const [, value] = match.split(/name\s*:\s*/);
    return value.replace(/^["'`]/, "").replace(/["'`]$/, "").trim();
  });
  return Array.from(new Set(names.filter(Boolean)));
}

function extractBelongTo(source: string): string[] {
  const matches = source.match(/belongTo\s*:\s*["'`](.+?)["'`]/g) || [];
  return Array.from(
    new Set(
      matches
        .map((match) => match.split(/belongTo\s*:\s*/)[1]?.replace(/^["'`]/, "").replace(/["'`]$/, "").trim())
        .filter(Boolean),
    ),
  );
}

function ratio(intersection: number, total: number): number {
  if (total === 0) {
    return 0;
  }
  return Number((intersection / total).toFixed(2));
}

export async function validateThreeLayerArchitecture(
  componentName: string,
  options: ArchitectureValidatorOptions,
): Promise<CoverageReport> {
  const [fieldSource, editSource, previewSource] = await Promise.all([
    readFileText(options.fieldsFile),
    readFileText(options.editFile),
    readFileText(options.previewFile),
  ]);

  const fieldNames = extractFieldNames(fieldSource);
  const editBelongTo = extractBelongTo(editSource);
  const previewBelongTo = extractBelongTo(previewSource);

  const editMatches = fieldNames.filter((name) => editBelongTo.includes(name));
  const previewMatches = fieldNames.filter((name) => previewBelongTo.includes(name));

  const orphanFields = fieldNames.filter((name) => !editBelongTo.includes(name) || !previewBelongTo.includes(name));
  const orphanEdits = editBelongTo.filter((name) => !fieldNames.includes(name));

  return {
    fieldsExists: fieldNames.length > 0,
    editExists: editBelongTo.length > 0,
    previewExists: previewBelongTo.length > 0,
    coverage: {
      fieldsToEdit: ratio(editMatches.length, fieldNames.length),
      fieldsToPreview: ratio(previewMatches.length, fieldNames.length),
    },
    orphanFields,
    orphanEdits,
  };
}

export async function summarizeCoverage(report: CoverageReport, componentName: string): Promise<string> {
  return [
    `组件 ${componentName} FieldNode → EditFields 覆盖率 ${report.coverage.fieldsToEdit}`,
    `FieldNode → Preview 覆盖率 ${report.coverage.fieldsToPreview}`,
    report.orphanFields.length ? `Orphan Fields: ${report.orphanFields.join(", ")}` : "",
  ]
    .filter(Boolean)
    .join("; ");
}
